////////////////////////////////////////////////////////////////////////////////
//
//	This file is part of Woozox, see <http://code.google.com/p/woozox/>.
//
//	Copyright 2008-2009 Florian David
//
//	Woozoo is free software: you can redistribute it and/or modify
//	it under the terms of the GNU Lesser General Public License as 
//	published by the Free Software Foundation, either version 3 of 
//	the License, or (at your option) any later version.
//
//	Woozoo is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public 
//	License along with Woozoo. If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////
package org.woozoo.xhtml.core.base.selector
{
	import org.woozoo.xhtml.core.base.Document;
	import org.woozoo.xhtml.core.manager.cache.CacheManager;
	import org.woozoo.xhtml.core.util.StringUtil;
	
	/**
	 * @private
	 * */
	public final class Selector
	{
		private var __trace : Boolean = false;
		private var __document : Document;
		private var __selector : String;

		public function Selector(selector : String, document : Document) {
			__document = document;
			__selector = __prepareSelector(selector);
			__debug('analyzing : ['+ __selector+']');
		}
		
		private function __prepareSelector(selector : String) : String {
			// strip
			selector = StringUtil.strip(selector);
			// remove double spaces [   ] => [ ]
			selector = selector.replace(/[ ]+/g, ' ');
			// remove space before and after >, +, ~
			selector = selector.replace(/[ ]*(>|\+|~)[ ]*/g, '$1');
			
			// add * before : if needed 
			// ex1: ':first' is '*:first-child'
			var char0 : String = selector.charAt(0);
			if (char0 == ":") {
				selector = '*'+selector;
			}
			selector = selector.replace(/[^a-zA-Z0-9\*"']+(:)/g, ' *$1');
			return selector;
		}
		
		public function select() : Array {
			var results : Array = [];
			if (!__document)
				return results;
			
			var cachedValue : Array = CacheManager.getInstance().getSelectorEntry(__document, __selector);
			if (cachedValue != null) {
				return cachedValue;
			}
			
			var selectorParser : SelectorParser = new SelectorParser();
			selectorParser.init(__selector);
			while (true) {
				var f : Boolean = selectorParser.next();
				var matchIndex : int = selectorParser.matchIndex;
				var analyse : String = selectorParser.analyse;
				if (matchIndex == -1) {
					__debug('Unable to understand "'+analyse+'" on selector "'+__selector+'"');
				} else {
					results = __selectedByIndexCache(selectorParser.index, matchIndex, results, analyse, selectorParser.selectorFoundNum);
				}	
				if (!f || results.length == 0) break;
			}
			__debug('Found '+results.length+' results');
			__debug('-----------------------------');
			return results;
		}  
		
		private function __selectedByIndexCache(index : int, matchIndex : int, result : Array, toAnalyse : String, foundSelectorNum : int) : Array {
			__debug('Analyse : '+toAnalyse);
			var selectorPart : String = __selector.slice(0, index);
			var unitCachedValue : Array = CacheManager.getInstance().getSelectorEntry(__document, selectorPart);
			if (unitCachedValue) {
				result = unitCachedValue;
			} else {
				result = __selectByIndex(matchIndex, result, toAnalyse, foundSelectorNum);
				CacheManager.getInstance().addSelectorEntry(__document, toAnalyse, result);
			}
			return result;
		}
		
		private function __selectByIndex(matchIndex : int, result : Array, analyse : String, foundSelectorNum : int) : Array {
			var selectorOperator : ISelectorOperator = SelectorOperatorFactory.factory(matchIndex);
			selectorOperator.debugFunction = __debug;
			selectorOperator.init(analyse, result, __document, foundSelectorNum);
			return selectorOperator.operate();
		}
		
		private function __debug(value : String) : void {
			if (__trace) {
				trace(value);
			}
		}
	}
}